#include "Mouse.hpp"

OIS::Mouse* OgreMax::Mouse::GetMouse()
{
    return this->mouse;
}

bool OgreMax::Mouse::IsNull() const
{
    return this->mouse == 0;
}

bool OgreMax::Mouse::PressedButton(OIS::MouseButtonID button) const
{
    return
        this->mouse != 0 &&
        !this->previousMouseState.buttonDown(button) &&
        this->mouse->getMouseState().buttonDown(button);
}

bool OgreMax::Mouse::PressingButton(OIS::MouseButtonID button) const
{
    return
        this->mouse != 0 &&
        this->mouse->getMouseState().buttonDown(button);
}

bool OgreMax::Mouse::ReleasedButton(OIS::MouseButtonID button) const
{
    return
        this->mouse != 0 &&
        this->previousMouseState.buttonDown(button) &&
        !this->mouse->getMouseState().buttonDown(button);
}

bool OgreMax::Mouse::PressedAnyButton() const
{
    for (int buttonIndex = 0; buttonIndex < BUTTON_COUNT; buttonIndex++)
    {
        if (PressedButton((OIS::MouseButtonID)buttonIndex))
            return true;
    }
    return false;
}

bool OgreMax::Mouse::PressingAnyButton() const
{
    return
        this->mouse != 0 &&
        this->mouse->getMouseState().buttons != 0;
}

bool OgreMax::Mouse::ReleasedAnyButton() const
{
    for (int buttonIndex = 0; buttonIndex < BUTTON_COUNT; buttonIndex++)
    {
        if (ReleasedButton((OIS::MouseButtonID)buttonIndex))
            return true;
    }
    return false;
}

bool OgreMax::Mouse::PressedAxis(Axis_t axis, int direction /*= 0*/) const
{
    return
        this->mouse != 0 &&
        axis < AXIS_COUNT &&
        !AxisActive(GetMouseAxis(this->previousMouseState, axis).rel, direction) &&
        AxisActive(GetMouseAxis(this->mouse->getMouseState(), axis).rel, direction);
}

bool OgreMax::Mouse::PressingAxis(Axis_t axis, int direction /*= 0*/) const
{
    return
        this->mouse != 0 &&
        axis < AXIS_COUNT &&
        AxisActive(GetMouseAxis(this->mouse->getMouseState(), axis).rel, direction);
}

bool OgreMax::Mouse::ReleasedAxis(Axis_t axis, int direction /*= 0*/) const
{
    return
        this->mouse != 0 &&
        axis < AXIS_COUNT &&
        AxisActive(GetMouseAxis(this->previousMouseState, axis).rel, direction) &&
        !AxisActive(GetMouseAxis(this->mouse->getMouseState(), axis).rel, direction);
}

bool OgreMax::Mouse::PressedAnyAxis() const
{
    bool pressed = false;

    if (this->mouse != 0)
    {
        const OIS::MouseState& mouseState = this->mouse->getMouseState();
        pressed =
            (!AxisActive(this->previousMouseState.X.rel) && AxisActive(mouseState.X.rel)) ||
            (!AxisActive(this->previousMouseState.Y.rel) && AxisActive(mouseState.Y.rel)) ||
            (!AxisActive(this->previousMouseState.Z.rel) && AxisActive(mouseState.Z.rel));
    }

    return pressed;
}

bool OgreMax::Mouse::PressingAnyAxis() const
{
    bool pressing = false;

    if (this->mouse != 0)
    {
        const OIS::MouseState& mouseState = this->mouse->getMouseState();
        pressing =
            AxisActive(mouseState.X.rel) ||
            AxisActive(mouseState.Y.rel) ||
            AxisActive(mouseState.Z.rel);
    }

    return pressing;
}

bool OgreMax::Mouse::ReleasedAnyAxis() const
{
    bool released = false;

    if (this->mouse != 0)
    {
        const OIS::MouseState& mouseState = this->mouse->getMouseState();
        released =
            (AxisActive(this->previousMouseState.X.rel) && !AxisActive(mouseState.X.rel)) ||
            (AxisActive(this->previousMouseState.Y.rel) && !AxisActive(mouseState.Y.rel)) ||
            (AxisActive(this->previousMouseState.Z.rel) && !AxisActive(mouseState.Z.rel));
    }

    return released;
}

int OgreMax::Mouse::GetPressedButton() const
{
    for (int buttonIndex = 0; buttonIndex < BUTTON_COUNT; buttonIndex++)
    {
        if (PressedButton((OIS::MouseButtonID)buttonIndex))
            return buttonIndex;
    }

    return -1;
}

int OgreMax::Mouse::GetPressingButton() const
{
    for (int buttonIndex = 0; buttonIndex < BUTTON_COUNT; buttonIndex++)
    {
        if (PressingButton((OIS::MouseButtonID)buttonIndex))
            return buttonIndex;
    }

    return -1;
}

int OgreMax::Mouse::GetReleasedButton() const
{
    for (int buttonIndex = 0; buttonIndex < BUTTON_COUNT; buttonIndex++)
    {
        if (ReleasedButton((OIS::MouseButtonID)buttonIndex))
            return buttonIndex;
    }

    return -1;
}

bool OgreMax::Mouse::GetPressedAxisIndexAndDirection(int& index, int& direction) const
{
    if (this->mouse != 0)
    {
        for (int axisIndex = 0; axisIndex < AXIS_COUNT; axisIndex++)
        {
            if (PressedAxis((Axis_t)axisIndex))
            {
                index = axisIndex;
                direction = (GetAxis((Axis_t)axisIndex).rel < 0) ? -1 : 1;
                return true;
            }
        }
    }

    return false;
}

bool OgreMax::Mouse::GetPressingAxisIndexAndDirection(int& index, int& direction) const
{
    if (this->mouse != 0)
    {
        for (int axisIndex = 0; axisIndex < AXIS_COUNT; axisIndex++)
        {
            if (PressingAxis((Axis_t)axisIndex))
            {
                index = axisIndex;
                direction = (GetAxis((Axis_t)axisIndex).rel < 0) ? -1 : 1;
                return true;
            }
        }
    }

    return false;
}

bool OgreMax::Mouse::GetReleasedAxisIndexAndDirection(int& index, int& direction) const
{
    if (this->mouse != 0)
    {
        for (int axisIndex = 0; axisIndex < AXIS_COUNT; axisIndex++)
        {
            if (ReleasedAxis((Axis_t)axisIndex))
            {
                index = axisIndex;
                direction = (GetAxis((Axis_t)axisIndex).rel < 0) ? -1 : 1;
                return true;
            }
        }
    }

    return false;
}

void OgreMax::Mouse::GetPressedButtons(InputSourceVector& inputSources) const
{
    InputSource inputSource;
    inputSource.deviceType = InputSource::MOUSE_BUTTON;
    for (inputSource.button = 0; inputSource.button < BUTTON_COUNT; inputSource.button++)
    {
        if (PressedButton((OIS::MouseButtonID)inputSource.button))
            inputSources.push_back(inputSource);
    }
}

void OgreMax::Mouse::GetPressingButtons(InputSourceVector& inputSources) const
{
    InputSource inputSource;
    inputSource.deviceType = InputSource::MOUSE_BUTTON;
    for (inputSource.button = 0; inputSource.button < BUTTON_COUNT; inputSource.button++)
    {
        if (PressingButton((OIS::MouseButtonID)inputSource.button))
            inputSources.push_back(inputSource);
    }
}

void OgreMax::Mouse::GetReleasedButtons(InputSourceVector& inputSources) const
{
    InputSource inputSource;
    inputSource.deviceType = InputSource::MOUSE_BUTTON;
    for (inputSource.button = 0; inputSource.button < BUTTON_COUNT; inputSource.button++)
    {
        if (ReleasedButton((OIS::MouseButtonID)inputSource.button))
            inputSources.push_back(inputSource);
    }
}

void OgreMax::Mouse::GetPressedAxisIndicesAndDirections(InputSourceVector& inputSources) const
{
    if (this->mouse != 0)
    {
        InputSource inputSource;
        inputSource.deviceType = InputSource::MOUSE_AXIS;
        for (inputSource.axis = 0; inputSource.axis < AXIS_COUNT; inputSource.axis++)
        {
            if (PressedAxis((Axis_t)inputSource.axis))
            {
                inputSource.direction = (GetAxis((Axis_t)inputSource.axis).rel < 0) ? -1 : 1;
                inputSources.push_back(inputSource);
            }
        }
    }
}

void OgreMax::Mouse::GetPressingAxisIndicesAndDirections(InputSourceVector& inputSources) const
{
    if (this->mouse != 0)
    {
        InputSource inputSource;
        inputSource.deviceType = InputSource::MOUSE_AXIS;
        for (inputSource.axis = 0; inputSource.axis < AXIS_COUNT; inputSource.axis++)
        {
            if (PressingAxis((Axis_t)inputSource.axis))
            {
                inputSource.direction = (GetAxis((Axis_t)inputSource.axis).rel < 0) ? -1 : 1;
                inputSources.push_back(inputSource);
            }
        }
    }
}

void OgreMax::Mouse::GetReleasedAxisIndicesAndDirections(InputSourceVector& inputSources) const
{
    if (this->mouse != 0)
    {
        InputSource inputSource;
        inputSource.deviceType = InputSource::MOUSE_AXIS;
        for (inputSource.axis = 0; inputSource.axis < AXIS_COUNT; inputSource.axis++)
        {
            if (ReleasedAxis((Axis_t)inputSource.axis))
            {
                inputSource.direction = (GetAxis((Axis_t)inputSource.axis).rel < 0) ? -1 : 1;
                inputSources.push_back(inputSource);
            }
        }
    }
}

bool OgreMax::Mouse::Moved() const
{
    return
        this->mouse != 0 &&
        (AxisActive(this->mouse->getMouseState().X.rel) || AxisActive(this->mouse->getMouseState().Y.rel));
}

const OIS::Axis& OgreMax::Mouse::GetAxis(Axis_t axis) const
{
    static OIS::Axis nullAxis;

    if (this->mouse != 0)
        return GetMouseAxis(this->mouse->getMouseState(), axis);
    else
        return nullAxis;
}

OgreMax::Mouse::Mouse()
{
    this->mouse = 0;
    this->updatedOnce = false;
}

void OgreMax::Mouse::Set(OIS::Mouse* mouse)
{
    this->mouse = mouse;
    this->updatedOnce = false;
}

void OgreMax::Mouse::Update()
{
    if (this->mouse != 0)
    {
        //Save current state as the previous state
        this->previousMouseState = this->mouse->getMouseState();

        //Get new state
        this->mouse->capture();

        //Copy current state to previous state if this is the first update
        if (!this->updatedOnce)
        {
            this->previousMouseState = this->mouse->getMouseState();
            this->updatedOnce = true;
        }
    }
}

const OIS::Axis& OgreMax::Mouse::GetMouseAxis(const OIS::MouseState& mouseState, Axis_t axis)
{
    static OIS::Axis nullAxis;

    switch (axis)
    {
    case AXIS_X: return mouseState.X;
    case AXIS_Y: return mouseState.Y;
    case AXIS_Z: return mouseState.Z;
    default: return nullAxis;
    }
}

bool OgreMax::Mouse::AxisActive(int value, int direction /*= 0*/, int deadZone /*= 0*/)
{
    if (direction == 0)
        return std::abs(value) > deadZone;
    else
        return value * direction > deadZone;
}